package com.cysoft.domain;

import com.cysoft.domain.solver.StartTimeUpdatingVariableListener;
import com.cysoft.domain.solver.TaskDifficultyComparator;
import org.optaplanner.core.api.domain.entity.PlanningEntity;
import org.optaplanner.core.api.domain.entity.PlanningPin;
import org.optaplanner.core.api.domain.variable.*;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * @author caiye
 */
@PlanningEntity(difficultyComparatorClass = TaskDifficultyComparator.class)
//@PlanningEntity
public class Task  extends Step {

    private int orderId;

    private String taskName;

    private String wheelCode;//wheel的id
    private Wheel wheel;

    private int orderPriority;
    private String customerName;


    private int count;


    /**
     * 0 自提
     * 1 物流
     */
    private int paymentType;

    @PlanningPin
    private boolean pinned;


    /**
     * 当前任务对应的轮型匹配的模具
     */
    private Mold mold;

    // Planning variables: changes during planning, between score calculations.
    //规划实体中的规划变量
    //这个规划变量的取值范围为employeeRange和taskRange，类型是TaskOrEmployee，表示当前Task的前一个步骤可能是另外一个Task也可能是一个employee
    //同时，这个规划变量是链式的，也就是运用了链模式，当然，在我们这里表示的是时间那么就可以说是链式模式在时间上的运用，也就是时间链模式
    @PlanningVariable(valueRangeProviderRefs = {"machineRange", "taskRange"}, graphType = PlanningVariableGraphType.CHAINED)
    private Step previousStep;

    // Shadow variables
    // Task nextTask inherited from superclass
    // 这是一个锚影子变量，意思是，这个employee变量是链式规划变量previousTaskOrEmployee的锚点，employee是锚
    // 源计划变量是，链接到锚的链式计划变量，也就是说，previousTaskOrEmployee会一个个的链接到employee锚变量上去
    @AnchorShadowVariable(sourceVariableName = "previousStep")
    private Machine machine;


    //表示这个property是一个或多个PlanningVariable的自定义阴影。开始时间是一个自定义影子变量
    //然后这里用到了两个参数
    //variableListenerClass是：源计划变量更改后，VariableListener会收到通知。然后可以根据需求进行相应的更改操作
    //sources是：触发更改此影子变量的源变量
    @CustomShadowVariable(variableListenerClass = StartTimeUpdatingVariableListener.class,
            // Arguable, to adhere to API specs (although this works), nextTask and employee should also be a source,
            // because this shadow must be triggered after nextTask and employee (but there is no need to be triggered by those)
            sources = {@PlanningVariableReference(variableName = "previousStep")})
    private LocalDateTime beginDateTime; // In minutes

    /**
     * 生产结束时间，计算后赋值
     */
    private LocalDateTime endDateTime;


    /**
     * 数据库字段，已知的交付日期
     */
    private LocalDateTime paymentTime;


    /**
     * 模具工作状态，每个模具工作96小时后需要休息6小时，否则任务需要停止。
     * 当模具都在休息时，延长工作任务时间，不进行任务的分割。
     */
    private Map<Integer,MoldWorkState> moldWorkStateMap;

    /**
     * 模具尺寸是否符合压铸台支持尺寸
     * @return
     */
    public boolean getMatchMoldSize() {
        if(mold == null || machine == null) {
            return false;
        }

        if (mold.size < machine.getMinSize() || mold.size > machine.getMaxSize()) {
            return false;
        }
        return true;
    }


    /**
     * 检查是否有可用模具
     */
    public boolean checkHasMold() {
        return  mold == null;
    }

    /**
     * 轮型铸造工艺要与机台一致
     * @return
     */
    public boolean getMatchWheelTechType() {
        if(wheel == null || machine == null) {
            return false;
        }

        for (WheelTechType type:machine.getWheelTechTypeList() ) {
            if(type.equals(wheel.getWheelTechType())) {
                return true;
            }
        }
        return false;
    }

    /**
     * 轮型浇口工艺要与机台一致
     * @return
     */
    public boolean getMatchFilterType() {
        if(wheel == null || machine == null) {
            return false;
        }
        for (FilterType type:wheel.getFilterTypeList() ) {
            if(!machine.getFilterTypeList().contains(type)) {
                return false;
            }
        }
        return true;
    }


    /**
     * 用于规则引擎软约束，越接近交付日期越好，延期最不好
     * @return
     */
    @Override
    public int getEndTimeDigtal() {
        if(endDateTime != null && paymentTime != null) {
            Duration duration = Duration.between(paymentTime,endDateTime);
            return (int) duration.getSeconds()/60;
        } else {
            return 0;
        }
    }

    /**
     * 任务花费时间：单位分钟
     */
    private int taskCoastTime = 0;
    /**
     * 自提交付一定不能延期
     * @return
     */
    public boolean getZiTiDelayed(){
        if(wheel == null) {
            return true;
        }
        taskCoastTime = (int)(count * wheel.getCostTime());
        if(beginDateTime != null) {
            //开始生产时间+生产时间 在交付时间后，认为延期
            return beginDateTime.plusMinutes(taskCoastTime).isAfter(getPaymentTime());
        }

        // 开始生产时间未确定，认为自提延期
        return true;
    }

    // 超时时间
    public int delayedTime(){
        if(endDateTime == null || paymentTime == null) {
            return  0;
        }
        Duration duration = Duration.between(paymentTime,endDateTime);
        long seconds = duration.getSeconds();
        if(seconds == 0) {
            return 0;
        }
        if(seconds > 0) {
            return (int)seconds;
        } else {
            // 没有延期，越接近交付日期越好，因此是负数45度斜线
            //
//            return (int)seconds;
            return (int)(172800/(double)seconds);
        }
    }


    public boolean machMold() {
        if(this.mold == null) {
            return false;
        }
        return this.wheelCode == this.mold.wheelCode;
    }

    public Task() {
        this.pinned = false;
    }

    public Task(long id, int orderId, String taskName, String wheelCode, int orderPriority
            , String customerName,LocalDateTime paymentDate, int count, int paymentType) {
        super(id);
        this.orderId = orderId;
        this.taskName = taskName;
        this.wheelCode = wheelCode;
        this.orderPriority = orderPriority;
        this.customerName = customerName;
        this.paymentTime = paymentDate;
        this.count = count;
        this.paymentType = paymentType;
    }

    public int getOrderId() {
        return orderId;
    }

    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }

    public String getTaskName() {
        return taskName;
    }

    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }

    public String getWheelCode() {
        return wheelCode;
    }

    public void setWheelCode(String wheelCode) {
        this.wheelCode = wheelCode;
    }

    public Wheel getWheel() {
        return wheel;
    }

    public void setWheel(Wheel wheel) {
        this.wheel = wheel;
    }

    public int getOrderPriority() {
        return orderPriority;
    }

    public void setOrderPriority(int orderPriority) {
        this.orderPriority = orderPriority;
    }

    public String getCustomerName() {
        return customerName;
    }

    public void setCustomerName(String customerName) {
        this.customerName = customerName;
    }

    /**
     * 交付日期
     * @return
     */
    public LocalDateTime getPaymentTime() {
        return paymentTime;
    }

    public void setPaymentTime(LocalDateTime paymentTime) {
        this.paymentTime = paymentTime;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getPaymentType() {
        return paymentType;
    }

    public void setPaymentType(int paymentType) {
        this.paymentType = paymentType;
    }

    public boolean isPinned() {
        return pinned;
    }

    public void setPinned(boolean pinned) {
        this.pinned = pinned;
    }

    public Step getPreviousStep() {
        return previousStep;
    }

    public void setPreviousStep(Step previousStep) {
        this.previousStep = previousStep;
    }

    @Override
    public Machine getMachine() {
        return machine;
    }

    public void setMachine(Machine machine) {
        this.machine = machine;
    }

    public LocalDateTime getBeginDateTime() {
        return beginDateTime;
    }

    public void setBeginDateTime(LocalDateTime beginDateTime) {
        this.beginDateTime = beginDateTime;
    }


    @Override
    public LocalDateTime getEndDateTime() {
        if(beginDateTime == null || wheel == null) {
            return null;
        }
        if(taskCoastTime == 0) {
            taskCoastTime = (int) (count * wheel.getCostTime());
            if (taskCoastTime > Constant.MOLD_CONTINUE_WORK_TIME) {
                // 暂时延长时间，不做更换模具
                taskCoastTime += Constant.MOLD_CONTINUE_SLEEP_TIME;
            }
        }
        if(this.endDateTime == null) {
            this.endDateTime = this.beginDateTime.plusMinutes(taskCoastTime);
        }
//        if(endDateTime.compareTo(LocalDateTime.of(2023,04,01,0,0)) > 0) {
//            System.out.println("异常时间开始："+beginDateTime.toString() + " , taskCoastTime:"+taskCoastTime + ", delayedTime:"+delayedTime());
//        }
        return endDateTime;
    }

    public void setEndDateTime(LocalDateTime endDateTime) {
        this.endDateTime = endDateTime;
    }

    @Override
    public Mold getMold() {
        return mold;
    }

    public void setMold(Mold mold) {
        this.mold = mold;
        if(this.mold != null) {
            this.mold.setTaskId(this.id);
        }
    }

    public Map<Integer, MoldWorkState> getMoldWorkStateMap() {
        return moldWorkStateMap;
    }

    public void setMoldWorkStateMap(Map<Integer, MoldWorkState> moldWorkStateMap) {
        this.moldWorkStateMap = moldWorkStateMap;
    }

    public int getTaskCoastTime() {
        return taskCoastTime;
    }

    public void setTaskCoastTime(int taskCoastTime) {
        this.taskCoastTime = taskCoastTime;
    }


}